using System.Collections.Generic;
using System.IO;
using System.Text;

namespace NFreeMarker.Template.Utility
{
	/**
	 * Performs an XML escaping of a given template fragment. Specifically,
	 * &lt; &gt; &quot ' and &amp; are all turned into entity references.
	 *
	 * <p>An instance of this transform is initially visible as shared
	 * variable called <tt>xml_escape</tt>.</p>
	 * 
	 * @version $Id: XmlEscape.java,v 1.29 2004/11/27 14:49:57 ddekany Exp $
	 */
	public class XmlEscape : ITemplateTransformModel
    {
	    private static readonly char[] Lt = "&lt;".ToCharArray();
	    private static readonly char[] Gt = "&gt;".ToCharArray();
	    private static readonly char[] Amp = "&amp;".ToCharArray();
	    private static readonly char[] Quot = "&quot;".ToCharArray();
	    private static readonly char[] Apos = "&apos;".ToCharArray();

	    public TextWriter GetWriter(TextWriter output, IDictionary<string, ITemplateModel> args)
	    {
	        return new XmlEscapeWriter(output);
	    }

	    private class XmlEscapeWriter : TextWriter
	    {
	        private readonly TextWriter _output;

	        public XmlEscapeWriter(TextWriter output)
	        {
	            _output = output;
	        }

	        public override Encoding Encoding
	        {
	            get { return _output.Encoding; }
	        }

	        public override void Write(int c)
            {
                switch ((char) c)
                {
                    case '<':
                        _output.Write(Lt, 0, 4);
                        break;
                    case '>':
                        _output.Write(Gt, 0, 4);
                        break;
                    case '&':
                        _output.Write(Amp, 0, 5);
                        break;
                    case '"':
                        _output.Write(Quot, 0, 6);
                        break;
                    case '\'':
                        _output.Write(Apos, 0, 6);
                        break;
                    default:
                        _output.Write(c);
                        break;
                }
            }

            public override void Write(char[] cbuf, int off, int len)
            {
                int lastoff = off;
                int lastpos = off + len;
                for (int i = off; i < lastpos; i++)
                {
                    switch (cbuf[i])
                    {
                        case '<':
                            _output.Write(cbuf, lastoff, i - lastoff);
                            _output.Write(Lt, 0, 4); lastoff = i + 1;
                            break;
                        case '>':
                            _output.Write(cbuf, lastoff, i - lastoff);
                            _output.Write(Gt, 0, 4); lastoff = i + 1;
                            break;
                        case '&':
                            _output.Write(cbuf, lastoff, i - lastoff);
                            _output.Write(Amp, 0, 5); lastoff = i + 1;
                            break;
                        case '"':
                            _output.Write(cbuf, lastoff, i - lastoff);
                            _output.Write(Quot, 0, 6); lastoff = i + 1;
                            break;
                        case '\'':
                            _output.Write(cbuf, lastoff, i - lastoff);
                            _output.Write(Apos, 0, 6); lastoff = i + 1;
                            break;
                    }
                }
                int remaining = lastpos - lastoff;
                if (remaining > 0)
                {
                    _output.Write(cbuf, lastoff, remaining);
                }
            }

            public override void Flush()
            {
                _output.Flush();
            }

            public override void Close()
            {
            }
	    }
    }
}